package com.qianyu.versioncontrolapi.service.impl;


import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qianyu.versioncontrolapi.service.VersionConfService;
import com.qianyu.versioncontrolcore.entity.base.BaseIdDTO;
import com.qianyu.versioncontrolcore.entity.bean.VersionConf;
import com.qianyu.versioncontrolcore.entity.dto.RegisterNumDto;
import com.qianyu.versioncontrolcore.entity.dto.VersionConfDto;
import com.qianyu.versioncontrolcore.exception.CustomerException;
import com.qianyu.versioncontrolapi.mapper.VersionConfMapper;
import com.qianyu.versioncontrolcore.util.CacheKeyUtil;
import com.qianyu.versioncontrolcore.util.HttpKit;
import com.qianyu.versioncontrolcore.util.Object2MapUtil;
import com.qianyu.versioncontrolcore.util.RedisUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.qianyu.versioncontrolcore.enumerate.ResultEnum.VERSION_GET_ERROR;
import static com.qianyu.versioncontrolcore.enumerate.ResultEnum.VERSION_IS_EXIST;

@Service
@RequiredArgsConstructor
public class VersionConfServiceImpl extends ServiceImpl<VersionConfMapper, VersionConf> implements VersionConfService {


    public final RedisUtil redisUtil;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveVersion(VersionConfDto.VersionConfSaveDto dto) {
        validateVersion(null, dto.getVersion());
        VersionConf conf = new VersionConf();
        BeanUtils.copyProperties(dto, conf);
        saveOrUpdate(conf);
        redisUtil.del(CacheKeyUtil.versionConfSortedSet());
        getVersion();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateVersion(VersionConfDto.VersionConfUpdateDto dto) {
        validateVersion(dto.getId(), dto.getVersion());
        VersionConf conf = new VersionConf();
        BeanUtils.copyProperties(dto, conf);
        saveOrUpdate(conf);
        redisUtil.del(CacheKeyUtil.versionConfSortedSet());
        getVersion();
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteVersion(BaseIdDTO dto) {
        update(Wrappers.<VersionConf>lambdaUpdate().set(VersionConf::getStatus, 0).eq(VersionConf::getId, dto.getId()));
        redisUtil.del(CacheKeyUtil.versionConfSortedSet());
        getVersion();
    }

    @Override
    public VersionConf getOne(BaseIdDTO dto) {
        return getById(dto.getId());
    }

    @Override
    public Page<VersionConf> pageList(VersionConfDto.VersionConfPageDto dto) {
        LambdaQueryWrapper<VersionConf> wrapper = Wrappers.lambdaQuery();
        if (!ObjectUtils.isEmpty(dto.getVersion())) {
            wrapper.like(VersionConf::getVersion, dto.getVersion());
        }
        return (Page<VersionConf>) page(new Page<>(dto.getPageNum(), dto.getPageSize()), wrapper);
    }

    @Override
    public String getVersion() {

        String version = null;

        if (redisUtil.hasKey(CacheKeyUtil.versionConfSortedSet())) {
            Object o = redisUtil.get(CacheKeyUtil.versionConfSortedSet());
            List<VersionConf> versionConfVos = (List<VersionConf>) o;
            //根据优先级分组
            Map<Integer, List<VersionConf>> map = versionConfVos.stream().collect(Collectors.groupingBy(VersionConf::getPriority));
            ArrayList<Integer> integers = new ArrayList<>(map.keySet());
            //优先级降序
            Collections.reverse(integers);
            one:
            for (Integer i : integers) {
                //获取优先级高的并打乱顺序
                List<VersionConf> voList = map.get(i);
                Collections.shuffle(voList);
                for (VersionConf conf : voList) {
                    //验证是否在有效期
                    if (redisUtil.hasKey(conf.getVersion())) {
                        //判断是否在间隔秒内
                        if (!redisUtil.hasKey(CacheKeyUtil.versionConfSpace(conf.getVersion()))) {
                            version = conf.getVersion();
                            redisUtil.set(CacheKeyUtil.versionConfSpace(conf.getVersion()), i, conf.getSpace());
                        } else {
                            continue;
                        }
                        break one;
                    }

                }

            }

        } else {
            LambdaQueryWrapper<VersionConf> wrapper = Wrappers.lambdaQuery();
            wrapper.select(VersionConf::getVersion, VersionConf::getTime, VersionConf::getBurst,
                            VersionConf::getPriority, VersionConf::getSpace).eq(VersionConf::getStatus, 1)
                    .orderByDesc(VersionConf::getPriority);
            List<VersionConf> configs = baseMapper.selectList(wrapper);
            for (VersionConf vc : configs) {
                Integer count = getRegisterNum(new RegisterNumDto(vc.getVersion(), vc.getTime()));
                if (count > vc.getBurst()) {
                    configs.remove(vc);
                }
                redisUtil.set(vc.getVersion(), count, vc.getTime());
            }
            Collections.shuffle(configs);
            VersionConf conf = configs.stream().max(Comparator.comparing(v -> v.getPriority())).get();
            redisUtil.set(CacheKeyUtil.versionConfSortedSet(), configs, 600);
            version = conf.getVersion();
            redisUtil.set(CacheKeyUtil.versionConfSpace(conf.getVersion()), conf.getPriority(), conf.getSpace());
        }
        if (version == null) {
            throw new CustomerException(VERSION_GET_ERROR);
        }
        return version;
    }

    @Override
    public Integer getRegisterNum(RegisterNumDto dto) {
        try {
            Map<String, String> map = Object2MapUtil.bean2Map(dto);
            String resp = HttpKit.get("http://118.178.125.179:7203/v1/account/query_reg_num", map);
            JSONObject jsonObject = JSONObject.parseObject(resp);
            return jsonObject.getInteger("data");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void updateRedisCache(String version) {

        if (redisUtil.setNxEx(version, 10)) {
            //todo
        } else {
            Object o = redisUtil.get(version);
            Integer count = (Integer) o;
            redisUtil.set(version, count + 1);
        }
    }

    private void validateVersion(Long id, String version) {
        VersionConf one = baseMapper.selectOne(Wrappers.<VersionConf>lambdaQuery().eq(VersionConf::getVersion, version)
                .ne(!ObjectUtils.isEmpty(id), VersionConf::getId, id));
        if (!ObjectUtils.isEmpty(one)) {
            throw new CustomerException(VERSION_IS_EXIST);
        }
    }
}
